﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Accona.Remoting.Remoting;

namespace Accona.Remoting
{
    public class RemoteExecutionRepository : IRepository, IResolver
    {
        private static readonly RemoteExecutionRepository instance = new RemoteExecutionRepository();
        private Dictionary<Type, Type> rep = new Dictionary<Type, Type>();
        private readonly Dictionary<Type, Delegate> _lambdaResolves = new Dictionary<Type, Delegate>();
        private readonly HashSet<Type> _supportedTypes = new HashSet<Type>();

        private RemoteExecutionRepository()
        {
        }

        public static RemoteExecutionRepository Instance
        {
            get { return instance; }
        }

        #region IRepository Members

        public T Resolve<T>()
        {
            Type result;
            Delegate lambdaDelegate;
            if (_lambdaResolves.TryGetValue(typeof(T), out lambdaDelegate))
            {
                return (T)lambdaDelegate.DynamicInvoke();
            }
            if (rep.TryGetValue(typeof (T), out result))
                return (T) Activator.CreateInstance(result);
            return default(T);
        }

        #endregion

        #region IResolver Members

        public object Resolve(Type objectType)
        {
            Delegate lambdaDelegate;
            if (_lambdaResolves.TryGetValue(objectType, out lambdaDelegate))
            {
                return lambdaDelegate.DynamicInvoke();
            }
            Type result;
            if (rep.TryGetValue(objectType, out result))
                return Activator.CreateInstance(result);
            return null;
        }

        #endregion

        public IList<Type> SupportedInterfaces()
        {
            return _supportedTypes.ToList();
        }

        public void ParseAssembly(Assembly assembly)
        {
            Type[] types = assembly.GetTypes();
            foreach (Type type in types)
            {
                object[] attrs = type.GetCustomAttributes(typeof (RemoteExecutionHandlerAttribute), false);
                if (attrs.Length > 0)
                {
                    var intrfc = ((RemoteExecutionHandlerAttribute) attrs[0]).Type;
                    rep.Add(intrfc, type);
                    _supportedTypes.Add(intrfc);
                }
            }
        }

        public void AddResolve<T>(Func<T> func)
        {
            this._lambdaResolves.Add(typeof(T), func);
            _supportedTypes.Add(typeof(T));
        }
    }
}